/*
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package commandhandlers;

import interfaces.TextHandler;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import main.Bot;
import main.CommonMethods;
import main.Main;
import main.UserCentral;

import org.apache.log4j.Logger;
import org.jibble.pircbot.Colors;

import output.IRCOutput;
import output.OutputTypeEnum;
import tools.StringTools;
import tools.UtopiaTime;
import botobjects.Army;
import botobjects.Attack;
import botobjects.InstantDamageSpellOp;
import botobjects.Province;
import botobjects.SpellOp;
import database.DBConnectionFactory;

/**
 * Handles inputs that are pasted from ingame, such as spells/ops, attacks and aid
 * 
 * 
 */
public class TextInputHandler implements TextHandler {
   /**
    * Represents a text item, which is a customized item that can be identified from pasted text. Currently used for spells and
    * ops
    * 
    * 
    */
   private static class TextItem {
	  private final String name;
	  private final String regex;
	  private String completed;
	  private final int type;

	  public TextItem(final String name, final String regex, final int type) {
		 this.name = name;
		 this.regex = regex;
		 this.type = type;
		 completed = regex;
		 completed = completed.replace("[province]", PROVINCEREGEX);
		 completed = completed.replace("[result]", RESULTREGEX);
		 completed = completed.replace("[duration]", DURATIONREGEX);
	  }

	  public String getCompletedRegex() {
		 return completed;
	  }

	  public String getName() {
		 return name;
	  }

	  public String getRegex() {
		 return regex;
	  }

	  public int getType() {
		 return type;
	  }
   }

   /**
    * Handles removing of incomplete armies if the return time isn't pasted within a certain time
    * 
    * 
    */
   private class ToDoTask extends TimerTask {
	  private final String user;

	  public ToDoTask(final String user) {
		 super();
		 this.user = user;
	  }

	  @Override
	  public void run() {
		 incompleteArmies.remove(user);
	  }
   }

   private static TextInputHandler INSTANCE = null;

   public static final TextInputHandler getInstance() {
	  if (INSTANCE == null) {
		 INSTANCE = new TextInputHandler();
	  }
	  return INSTANCE;
   }

   public static final TextInputHandler newInstance() {
	  INSTANCE = null;
	  return getInstance();
   }

   private final Map<String, Attack> incompleteArmies;

   private static final String DURATIONREGEX = "[0-9]{1,2}";

   private static final String RESULTREGEX = "[0-9,]{1,}";

   private static final String PROVINCEREGEX = ".*\\([0-9]{1,2}(:)[0-9]{1,2}\\)";

   private TextInputHandler() {
	  incompleteArmies = new HashMap<String, Attack>();
   }

   /**
    * Returns a list of text items, i.e. customized regexes to capture various things from ingame messages
    * 
    * @return
    */
   private List<TextItem> getTextItems() {
	  List<TextItem> out = new ArrayList<TextItem>();
	  PreparedStatement ps = null;
	  ResultSet rs = null;
	  try {
		 ps = DBConnectionFactory.getConnectionHandler().getConnection().prepareStatement("SELECT * FROM textinput");
		 rs = ps.executeQuery();

		 while (rs.next()) {
			out.add(new TextItem(rs.getString("name"), rs.getString("regex"), rs.getInt("inputtype")));
		 }
	  } catch (final SQLException e) {
		 Logger.getLogger("Error").error("", e);
	  } finally {
		 try {
			rs.close();
		 } catch (final Exception e) {
		 }
		 try {
			ps.close();
		 } catch (final Exception e) {
		 }
	  }
	  return out;
   }

   public boolean handleText(final String msg, final String channel, final String sender, final IRCOutput out) {
	  boolean handled = false;
	  String message = msg.trim();
	  if (message.charAt(0) == '*') {
		 message = message.substring(1);
	  }
	  message = message.trim();
	  String user = UserCentral.getInstance().getOnlineUser(sender);
	  if (message.matches("Your forces arrive at .*\\([0-9]{1,2}(:)[0-9]{1,2}\\).*")) {
		 Long timeofAttack = GregorianCalendar.getInstance().getTimeInMillis();
		 String prov = message.substring(message.indexOf("Your forces arrive at "));
		 prov = prov.replace("Your forces arrive at ", "");
		 String kdloc = prov;
		 prov = prov.substring(0, prov.indexOf('(')).trim();
		 kdloc = kdloc.replace(prov, "");
		 kdloc = kdloc.substring(0, kdloc.indexOf(')') + 1).trim();

		 Attack attack;
		 attack = new Attack(user, prov, kdloc, timeofAttack, "", 0, "");

		 incompleteArmies.put(user, attack);
		 Timer timer = new Timer();
		 timer.schedule(new ToDoTask(user), 4000);
		 handled = true;
	  }
	  if (message.matches(".*Your army has taken [0-9,]{1,} .*")) {
		 Long timeofAttack = GregorianCalendar.getInstance().getTimeInMillis();
		 String prov = "";
		 String kdloc = "";
		 Attack temp = incompleteArmies.get(user);
		 if (temp != null) {
			prov = temp.getDefender();
			kdloc = temp.getDefenderLoc();
		 }
		 String gain = message.substring(message.indexOf("Your army has taken "));
		 gain = gain.replace("Your army has taken ", "");
		 gain = gain.substring(0, gain.indexOf(' '));
		 gain = gain.replace(",", "").trim() + " acres gained";

		 if (message.matches(".*Our forces will be available again in ([0-9]{1,2}(\\.)[0-9]{1,2}) .*")
			   || message.matches(".*Taking full control of your new land will take ([0-9]{1,2}(\\.)[0-9]{1,2}) .*")) {
			String time = "";
			if (message.matches(".*Our forces will be available again in ([0-9]{1,2}(\\.)[0-9]{1,2}) .*")) {
			   time = message.substring(message.indexOf("available again in "));
			   time = time.replace("available again in ", "");
			   time = time.substring(0, time.indexOf(' ')).trim();
			} else {
			   time = message.substring(message.indexOf("land will take "));
			   time = time.replace("land will take ", "");
			   time = time.substring(0, time.indexOf(' ')).trim();
			}

			Long home = (long) (Double.valueOf(time) * Main.getBotSettings().getTickLength() * 60 * 1000 + GregorianCalendar
				  .getInstance().getTimeInMillis());

			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, home, 0, "TM");

			attack.saveToDB();
			int armyno = ArmyHandler.findFreeGeneral(attack.getAttacker());
			Army army = new Army(user, armyno, home, timeofAttack, 0, 4, gain);

			KDHandler.setReadiness(user, true, "attack");
			KDHandler.updateClaimed(prov);
			ChainHandler.updateTargetList(attack.getDefender(), attack.getAttacker());

			if (army.saveToDB()) {
			   Bot.getInstance().sendMessage(
					 Main.getBotSettings().getMainChannel(),
					 Colors.OLIVE + "Army added for " + Colors.RED + army.getMainNick() + Colors.OLIVE + ", returns in "
						   + Colors.BLUE + UtopiaTime.compareTimes(army.getArmyHome()));
			   out.addOutput("Use !mo <offense> <generals> to add more info to the attack");
			   out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
			   out.setTarget(sender);
			   incompleteArmies.remove(user);
			}
		 } else {
			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, 0, "TM");

			attack.saveToDB();

			incompleteArmies.put(user, attack);
			Timer timer = new Timer();
			timer.schedule(new ToDoTask(user), 4000);
		 }
		 handled = true;
	  } else if (message.matches(".*(Our army broke our enemy's initial "
			+ "defenses but was held from going further.|Our troops had a moderate amount of success getting behind "
			+ "enemy defenses.|Our troops were able to get a small foothold into enemy territory.).*")) {
		 Long timeofAttack = GregorianCalendar.getInstance().getTimeInMillis();
		 String prov = "";
		 String kdloc = "";
		 Attack temp = incompleteArmies.get(user);
		 if (temp != null) {
			prov = temp.getDefender();
			kdloc = temp.getDefenderLoc();
			incompleteArmies.remove(user);
		 }

		 Attack attack;
		 attack = new Attack(user, prov, kdloc, timeofAttack, "0", 0, "Conquest");

		 attack.saveToDB();

		 incompleteArmies.put(user, attack);
		 Timer timer = new Timer();
		 timer.schedule(new ToDoTask(user), 4000);
		 handled = true;
	  } else if (message.matches(".*Your army has recaptured [0-9,]{1,} .*")) {
		 Long timeofAttack = GregorianCalendar.getInstance().getTimeInMillis();
		 String prov = "";
		 String kdloc = "";
		 Attack temp = incompleteArmies.get(user);
		 if (temp != null) {
			prov = temp.getDefender();
			kdloc = temp.getDefenderLoc();
			incompleteArmies.remove(user);
		 }
		 String gain = message.substring(message.indexOf("Your army has recaptured "));
		 gain = gain.replace("Your army has recaptured ", "");
		 gain = gain.substring(0, gain.indexOf(' '));
		 gain = gain.replace(",", "").trim() + " acres reclaimed";

		 if (message.matches(".*Our forces will be available again in ([0-9]{1,2}(\\.)[0-9]{1,2}) .*")
			   || message.matches(".*Taking full control of your new land will take ([0-9]{1,2}(\\.)[0-9]{1,2}) .*")) {
			String time = "";
			if (message.matches(".*Our forces will be available again in ([0-9]{1,2}(\\.)[0-9]{1,2}) .*")) {
			   time = message.substring(message.indexOf("available again in "));
			   time = time.replace("available again in ", "");
			   time = time.substring(0, time.indexOf(' ')).trim();
			} else {
			   time = message.substring(message.indexOf("land will take "));
			   time = time.replace("land will take ", "");
			   time = time.substring(0, time.indexOf(' ')).trim();
			}

			Long home = (long) (Double.valueOf(time) * Main.getBotSettings().getTickLength() * 60 * 1000 + GregorianCalendar
				  .getInstance().getTimeInMillis());

			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, home, 0, "Ambush");

			attack.saveToDB();
			int armyno = ArmyHandler.findFreeGeneral(attack.getAttacker());
			Army army = new Army(user, armyno, home, timeofAttack, 0, 4, gain);

			KDHandler.setReadiness(user, true, "attack");
			KDHandler.updateClaimed(prov);
			ChainHandler.updateTargetList(attack.getDefender(), attack.getAttacker());

			if (army.saveToDB()) {
			   Bot.getInstance().sendMessage(
					 Main.getBotSettings().getMainChannel(),
					 Colors.OLIVE + "Army added for " + Colors.RED + army.getMainNick() + Colors.OLIVE + ", returns in "
						   + Colors.BLUE + UtopiaTime.compareTimes(army.getArmyHome()));
			   out.addOutput("Use !mo <offense> <generals> to add more info to the attack");
			   out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
			   out.setTarget(sender);
			   incompleteArmies.remove(user);
			}
		 } else {
			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, 0, "Ambush");

			attack.saveToDB();

			incompleteArmies.put(user, attack);
			Timer timer = new Timer();
			timer.schedule(new ToDoTask(user), 4000);
		 }
		 handled = true;
	  } else if (message.matches(".*Your army burned and (razed|destroyed) [0-9,]{1,} .*")) {
		 Long timeofAttack = GregorianCalendar.getInstance().getTimeInMillis();
		 String prov = "";
		 String kdloc = "";
		 Attack temp = incompleteArmies.get(user);
		 if (temp != null) {
			prov = temp.getDefender();
			kdloc = temp.getDefenderLoc();
			incompleteArmies.remove(user);
		 }
		 String gain = message.substring(message.indexOf("Your army burned and"));
		 gain = gain.replaceAll("Your army burned and (razed|destroyed) ", "");
		 gain = gain.substring(0, gain.indexOf(' '));
		 gain = gain.replace(",", "").trim() + " acres razed";

		 if (message.matches(".*Our forces will be available again in ([0-9]{1,2}(\\.)[0-9]{1,2}) .*")) {
			String time = message.substring(message.indexOf("available again in "));
			time = time.replace("available again in ", "");
			time = time.substring(0, time.indexOf(' ')).trim();

			Long home = (long) (Double.valueOf(time) * Main.getBotSettings().getTickLength() * 60 * 1000 + GregorianCalendar
				  .getInstance().getTimeInMillis());

			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, home, 0, "Raze");

			attack.saveToDB();
			int armyno = ArmyHandler.findFreeGeneral(attack.getAttacker());
			Army army = new Army(user, armyno, home, timeofAttack, 0, 4, gain);

			KDHandler.setReadiness(user, true, "attack");
			KDHandler.updateClaimed(prov);
			ChainHandler.updateTargetList(attack.getDefender(), attack.getAttacker());

			if (army.saveToDB()) {
			   Bot.getInstance().sendMessage(
					 Main.getBotSettings().getMainChannel(),
					 Colors.OLIVE + "Army added for " + Colors.RED + army.getMainNick() + Colors.OLIVE + ", returns in "
						   + Colors.BLUE + UtopiaTime.compareTimes(army.getArmyHome()));
			   out.addOutput("Use !mo <offense> <generals> to add more info to the attack");
			   out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
			   out.setTarget(sender);
			   incompleteArmies.remove(user);
			}
		 } else {
			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, 0, "Raze");

			attack.saveToDB();

			incompleteArmies.put(user, attack);
			Timer timer = new Timer();
			timer.schedule(new ToDoTask(user), 4000);
		 }
		 handled = true;
	  } else if (message.matches(".*Your army stole [0-9,]{1,} .*")) {
		 Long timeofAttack = GregorianCalendar.getInstance().getTimeInMillis();
		 String prov = "";
		 String kdloc = "";
		 Attack temp = incompleteArmies.get(user);
		 if (temp != null) {
			prov = temp.getDefender();
			kdloc = temp.getDefenderLoc();
			incompleteArmies.remove(user);
		 }
		 String gain = message.substring(message.indexOf("Your army stole "));
		 gain = gain.replaceAll("Your army stole ", "");
		 gain = gain.substring(0, gain.indexOf(' '));
		 gain = gain.replace(",", "").trim() + " books stolen";

		 if (message.matches(".*Our forces will be available again in ([0-9]{1,2}(\\.)[0-9]{1,2}) .*")) {
			String time = message.substring(message.indexOf("available again in "));
			time = time.replace("available again in ", "");
			time = time.substring(0, time.indexOf(' ')).trim();

			Long home = (long) (Double.valueOf(time) * Main.getBotSettings().getTickLength() * 60 * 1000 + GregorianCalendar
				  .getInstance().getTimeInMillis());

			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, home, 0, "Learn");

			attack.saveToDB();
			int armyno = ArmyHandler.findFreeGeneral(attack.getAttacker());
			Army army = new Army(user, armyno, home, timeofAttack, 0, 4, gain);

			KDHandler.setReadiness(user, true, "attack");
			KDHandler.updateClaimed(prov);
			ChainHandler.updateTargetList(attack.getDefender(), attack.getAttacker());

			if (army.saveToDB()) {
			   Bot.getInstance().sendMessage(
					 Main.getBotSettings().getMainChannel(),
					 Colors.OLIVE + "Army added for " + Colors.RED + army.getMainNick() + Colors.OLIVE + ", returns in "
						   + Colors.BLUE + UtopiaTime.compareTimes(army.getArmyHome()));
			   out.addOutput("Use !mo <offense> <generals> to add more info to the attack");
			   out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
			   out.setTarget(sender);
			   incompleteArmies.remove(user);
			}
		 } else {
			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, 0, "Learn");

			attack.saveToDB();

			incompleteArmies.put(user, attack);
			Timer timer = new Timer();
			timer.schedule(new ToDoTask(user), 4000);
		 }
		 handled = true;
	  } else if (message.matches(".*Your army massacred [0-9,]{1,} .*")) {
		 Long timeofAttack = GregorianCalendar.getInstance().getTimeInMillis();
		 String prov = "";
		 String kdloc = "";
		 Attack temp = incompleteArmies.get(user);
		 if (temp != null) {
			prov = temp.getDefender();
			kdloc = temp.getDefenderLoc();
			incompleteArmies.remove(user);
		 }
		 String gain = message.substring(message.indexOf("Your army massacred "));
		 gain = gain.replaceAll("Your army massacred ", "");
		 gain = gain.substring(0, gain.indexOf(' '));
		 gain = gain.replace(",", "").trim() + " killed";

		 if (message.matches(".*Our forces will be available again in ([0-9]{1,2}(\\.)[0-9]{1,2}).*")) {
			String time = message.substring(message.indexOf("available again in "));
			time = time.replace("available again in ", "");
			time = time.substring(0, time.indexOf(' ')).trim();

			Long home = (long) (Double.valueOf(time) * Main.getBotSettings().getTickLength() * 60 * 1000 + GregorianCalendar
				  .getInstance().getTimeInMillis());

			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, home, 0, "Massacre");

			attack.saveToDB();
			int armyno = ArmyHandler.findFreeGeneral(attack.getAttacker());
			Army army = new Army(user, armyno, home, timeofAttack, 0, 4, gain);

			KDHandler.setReadiness(user, true, "attack");
			KDHandler.updateClaimed(prov);
			ChainHandler.updateTargetList(attack.getDefender(), attack.getAttacker());

			if (army.saveToDB()) {
			   Bot.getInstance().sendMessage(
					 Main.getBotSettings().getMainChannel(),
					 Colors.OLIVE + "Army added for " + Colors.RED + army.getMainNick() + Colors.OLIVE + ", returns in "
						   + Colors.BLUE + UtopiaTime.compareTimes(army.getArmyHome()));
			   out.addOutput("Use !mo <offense> <generals> to add more info to the attack");
			   out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
			   out.setTarget(sender);
			   incompleteArmies.remove(user);
			}
		 } else {
			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, 0, "Massacre");

			attack.saveToDB();

			incompleteArmies.put(user, attack);
			Timer timer = new Timer();
			timer.schedule(new ToDoTask(user), 4000);
		 }
		 handled = true;
	  } else if (message.matches(".*Your army looted [0-9,]{1,} .*")) {
		 Long timeofAttack = GregorianCalendar.getInstance().getTimeInMillis();
		 String prov = "";
		 String kdloc = "";
		 Attack temp = incompleteArmies.get(user);
		 if (temp != null) {
			prov = temp.getDefender();
			kdloc = temp.getDefenderLoc();
			incompleteArmies.remove(user);
		 }
		 String gain = message.substring(message.indexOf("Your army looted "));
		 gain = gain.replaceAll("Your army looted ", "");
		 gain = gain.substring(0, gain.indexOf(' '));
		 gain = gain.replace(",", "").trim() + " gc stolen";

		 if (message.matches(".*Our forces will be available again in ([0-9]{1,2}(\\.)[0-9]{1,2}) .*")) {
			String time = message.substring(message.indexOf("available again in "));
			time = time.replace("available again in ", "");
			time = time.substring(0, time.indexOf(' ')).trim();

			Long home = (long) (Double.valueOf(time) * Main.getBotSettings().getTickLength() * 60 * 1000 + GregorianCalendar
				  .getInstance().getTimeInMillis());

			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, home, 0, "Plunder");

			attack.saveToDB();
			int armyno = ArmyHandler.findFreeGeneral(attack.getAttacker());
			Army army = new Army(user, armyno, home, timeofAttack, 0, 4, gain);

			KDHandler.setReadiness(user, true, "attack");
			KDHandler.updateClaimed(prov);
			ChainHandler.updateTargetList(attack.getDefender(), attack.getAttacker());

			if (army.saveToDB()) {
			   Bot.getInstance().sendMessage(
					 Main.getBotSettings().getMainChannel(),
					 Colors.OLIVE + "Army added for " + Colors.RED + army.getMainNick() + Colors.OLIVE + ", returns in "
						   + Colors.BLUE + UtopiaTime.compareTimes(army.getArmyHome()));
			   out.addOutput("Use !mo <offense> <generals> to add more info to the attack");
			   out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
			   out.setTarget(sender);
			   incompleteArmies.remove(user);
			}
		 } else {
			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, 0, "Plunder");

			attack.saveToDB();

			incompleteArmies.put(user, attack);
			Timer timer = new Timer();
			timer.schedule(new ToDoTask(user), 4000);
		 }
		 handled = true;
	  } else if (message.matches("Your army was no match for the defenses of .*\\([0-9]{1,2}(:)" + "[0-9]{1,2}\\).*")) {
		 Long timeofAttack = GregorianCalendar.getInstance().getTimeInMillis();
		 String prov = "";
		 String kdloc = "";
		 Attack temp = incompleteArmies.get(user);
		 if (temp != null) {
			prov = temp.getDefender();
			kdloc = temp.getDefenderLoc();
			incompleteArmies.remove(user);
		 }
		 String gain = "ticket to the failboat";

		 if (message.matches(".*Our forces will be available again in ([0-9]{1,2}(\\.)[0-9]{1,2}) .*")) {
			String time = message.substring(message.indexOf("available again in "));
			time = time.replace("available again in ", "");
			time = time.substring(0, time.indexOf(' ')).trim();

			Long home = (long) (Double.valueOf(time) * Main.getBotSettings().getTickLength() * 60 * 1000 + GregorianCalendar
				  .getInstance().getTimeInMillis());

			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, home, 0, "Bounce");

			attack.saveToDB();
			int armyno = ArmyHandler.findFreeGeneral(attack.getAttacker());
			Army army = new Army(user, armyno, home, timeofAttack, 0, 4, gain);

			KDHandler.setReadiness(user, true, "attack");
			KDHandler.updateClaimed(prov);
			ChainHandler.updateTargetList(attack.getDefender(), attack.getAttacker());

			if (army.saveToDB()) {
			   Bot.getInstance().sendMessage(
					 Main.getBotSettings().getMainChannel(),
					 Colors.OLIVE + "Army added for " + Colors.RED + army.getMainNick() + Colors.OLIVE + ", returns in "
						   + Colors.BLUE + UtopiaTime.compareTimes(army.getArmyHome()));
			   out.addOutput("Use !mo <offense> <generals> to add more info to the attack");
			   out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
			   out.setTarget(sender);
			   incompleteArmies.remove(user);
			}
		 } else {
			Attack attack;
			attack = new Attack(user, prov, kdloc, timeofAttack, gain, 0, "Bounce");

			attack.saveToDB();

			incompleteArmies.put(user, attack);
			Timer timer = new Timer();
			timer.schedule(new ToDoTask(user), 4000);
		 }
		 handled = true;
	  } else if (message.matches(".*Our forces will be available again in ([0-9]{1,2}(\\.)[0-9]{1,2}) .*")) {
		 Attack attack = incompleteArmies.get(user);
		 if (attack == null)
			return false;

		 String armyreturn = message.replaceAll(".*Our forces will be available again in ", "");
		 armyreturn = armyreturn.substring(0, armyreturn.indexOf(' '));
		 Long timeOfReturn = (long) (Double.valueOf(armyreturn) * Main.getBotSettings().getTickLength() * 60 * 1000 + GregorianCalendar
			   .getInstance().getTimeInMillis());
		 attack.setTimeOfReturn(timeOfReturn);

		 attack.saveToDB();
		 int armyno = ArmyHandler.findFreeGeneral(attack.getAttacker());
		 Army army = new Army(user, armyno, timeOfReturn, attack.getTimeOfAttack(), 0, 4,
			   "Bounce".equals(attack.getAttackType()) ? "0" : attack.getGain());

		 KDHandler.setReadiness(user, true, "attack");
		 KDHandler.updateClaimed(attack.getDefender());
		 ChainHandler.updateTargetList(attack.getDefender(), attack.getAttacker());

		 if (army.saveToDB()) {
			Bot.getInstance().sendMessage(
				  Main.getBotSettings().getMainChannel(),
				  Colors.OLIVE + "Army added for " + Colors.RED + army.getMainNick() + Colors.OLIVE + ", returns in "
						+ Colors.BLUE + UtopiaTime.compareTimes(army.getArmyHome()));
			out.addOutput("Use !mo <offense> <generals> to add more info to the attack");
			out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
			out.setTarget(sender);
			incompleteArmies.remove(user);
		 }
		 handled = true;
	  } else if (message.matches("We have sent .* to .*\\([0-9]{1,2}(:)[0-9]{1,2}\\).*")) {
		 String prov = message.substring(message.indexOf("to") + 2);
		 prov = prov.substring(0, prov.indexOf('(')).trim();
		 String temp = "";
		 int food = 0;
		 int gold = 0;
		 int runes = 0;
		 int solds = 0;
		 boolean inputCorrect = false;
		 message = message.substring(0, message.indexOf(')') + 1);
		 try {
			if (message.matches(".*[0-9,]{1,} bushel.*")) {
			   temp = message.substring(0, message.indexOf("bushel")).trim();
			   temp = new StringBuffer(temp).reverse().toString();
			   temp = temp.substring(0, temp.indexOf(' '));
			   temp = new StringBuffer(temp).reverse().toString();
			   food = Integer.parseInt(temp.replace(",", ""));
			   inputCorrect = true;
			}
			if (message.matches(".*[0-9,]{1,} gold coin.*")) {
			   temp = message.substring(0, message.indexOf("gold coin")).trim();
			   temp = new StringBuffer(temp).reverse().toString();
			   temp = temp.substring(0, temp.indexOf(' '));
			   temp = new StringBuffer(temp).reverse().toString();
			   gold = Integer.parseInt(temp.replace(",", ""));
			   inputCorrect = true;
			}
			if (message.matches(".*[0-9,]{1,} rune.*")) {
			   temp = message.substring(0, message.indexOf("rune")).trim();
			   temp = new StringBuffer(temp).reverse().toString();
			   temp = temp.substring(0, temp.indexOf(' '));
			   temp = new StringBuffer(temp).reverse().toString();
			   runes = Integer.parseInt(temp.replace(",", ""));
			   inputCorrect = true;
			}
			if (message.matches(".*[0-9,]{1,} soldier.*")) {
			   temp = message.substring(0, message.indexOf("soldier")).trim();
			   temp = new StringBuffer(temp).reverse().toString();
			   temp = temp.substring(0, temp.indexOf(' '));
			   temp = new StringBuffer(temp).reverse().toString();
			   solds = Integer.parseInt(temp.replace(",", ""));
			   inputCorrect = true;
			}
		 } catch (NumberFormatException e) {
			Logger.getLogger("Error").error("", e);
		 }

		 KDHandler.setReadiness(user, true, "aid");

		 if (!inputCorrect)
			return false;
		 out.addOutput(KDHandler.registerAidPackage(food, gold, runes, solds, prov));
		 out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
		 out.setTarget(sender);
		 handled = true;
	  } else if (message.matches(".*Our thieves have infiltrated the Thieves' Guilds of .*\\([0-9]{1,2}(:)[0-9]{1,2}\\)\\. "
			+ "They appear to have about [0-9,]{1,} thieves.*")) {
		 String prov = message.replaceAll(".*Our thieves have infiltrated the Thieves' Guilds of ", "");
		 prov = prov.substring(0, prov.indexOf('(')).trim();
		 String thieves = message.substring(message.indexOf("They appear to have about "));
		 thieves = thieves.replaceAll("[^0-9]", "");

		 if (Province.updateThieves(prov, Integer.parseInt(thieves))) {
			CommonMethods.addAction(UserCentral.getInstance().getOnlineUser(sender), "Infiltrate");
			out.addOutput("Thieves updated for " + prov);
			out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_MESSAGE);
			out.setTarget(channel);
			handled = true;
		 }
	  } else if (message.matches(".*You send out [0-9,]{1,} (troops|troop) to fight the dragon.* but the dragon "
			+ "is weakened by [0-9,]{1,} points.*")) {
		 String points = message.replaceAll(".*is weakened by ", "");
		 points = points.replaceFirst(" points.*", "");
		 points = points.replaceAll("[^0-9]", "");
		 KDHandler.updateDragon(user, 1, Integer.parseInt(points));
		 out.addOutput(Colors.RED + "Dragon killing registered for " + sender);
		 out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_MESSAGE);
		 out.setTarget(channel);
		 handled = true;
	  } else if (message.matches(".*You have donated [0-9,]{1,} gold (coin|coins) to the quest of launching a dragon.*")) {
		 String donation = message.replaceAll(".*have donated ", "");
		 donation = donation.replaceFirst(" gold (coin|coins) to.*", "");
		 donation = donation.replaceAll("[^0-9]", "");
		 KDHandler.updateDragon(user, 2, Integer.parseInt(donation));
		 out.addOutput(Colors.RED + "Dragon donation registered for " + sender);
		 out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_MESSAGE);
		 out.setTarget(channel);
		 handled = true;
	  } else {
		 for (TextItem textItem : getTextItems()) {
			if (message.matches(textItem.getCompletedRegex())) {
			   if (textItem.getType() == 1) {
				  // self spell
				  String duration = textItem.getRegex().substring(0, textItem.getRegex().indexOf("[duration]"));
				  duration = message.replaceFirst(duration, "");
				  duration = StringTools.extractPartialString(DURATIONREGEX, duration);
				  Long endtime = GregorianCalendar.getInstance().getTimeInMillis()
						+ (long) ((Double.parseDouble(duration) + 1) * Main.getBotSettings().getTickLength() * 60 * 1000);
				  endtime = endtime - endtime % (Main.getBotSettings().getTickLength() * 60L * 1000);
				  SpellOp temp = new SpellOp(CommonMethods.getProv(user), "(" + Main.getBotSettings().getKdLoc() + ")",
						textItem.getName(), endtime, sender);
				  if (temp.saveToDB()) {
					 out.addOutput(temp.printSave());
					 out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
					 out.setTarget(sender);
					 handled = true;
				  }
			   } else if (textItem.getType() == 2) {
				  // duration spell/op on enemy
				  String province = textItem.getRegex().replace("[duration]", DURATIONREGEX);
				  province = province.substring(0, province.indexOf("[province]"));
				  province = province.replace("[province]", PROVINCEREGEX);
				  province = message.replaceFirst(province, "");
				  province = StringTools.extractPartialString(PROVINCEREGEX, province);
				  String kd = province.substring(province.indexOf('('));
				  province = province.substring(0, province.indexOf('(')).trim();
				  String duration = textItem.getRegex().substring(0, textItem.getRegex().indexOf("[duration]"));
				  duration = duration.replace("[province]", PROVINCEREGEX);
				  duration = duration.replace("[duration]", DURATIONREGEX);
				  duration = message.replaceFirst(duration, "");
				  duration = StringTools.extractPartialString(DURATIONREGEX, duration);
				  Long endtime = GregorianCalendar.getInstance().getTimeInMillis()
						+ (long) ((Double.parseDouble(duration) + 1) * Main.getBotSettings().getTickLength() * 60 * 1000);
				  endtime = endtime - endtime % (Main.getBotSettings().getTickLength() * 60L * 1000);
				  SpellOp temp = new SpellOp(province, kd, textItem.getName(), endtime, sender);
				  if (temp.saveToDB()) {
					 out.addOutput(temp.printSave());
					 out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
					 out.setTarget(sender);
					 Bot.getInstance().sendMessage(Main.getBotSettings().getMainChannel(), temp.printSave());
					 handled = true;
				  }
			   } else if (textItem.getType() == 3) {
				  // duration spell/op on enemy without province name in
				  // it
				  String duration = textItem.getRegex().substring(0, textItem.getRegex().indexOf("[duration]"));
				  duration = message.replaceFirst(duration, "");
				  duration = StringTools.extractPartialString(DURATIONREGEX, duration);
				  Long endtime = GregorianCalendar.getInstance().getTimeInMillis()
						+ (long) ((Double.parseDouble(duration) + 1) * Main.getBotSettings().getTickLength() * 60 * 1000);
				  endtime = endtime - endtime % (Main.getBotSettings().getTickLength() * 60L * 1000);
				  SpellOp temp = new SpellOp("", "", textItem.getName(), endtime, sender);
				  if (temp.saveToDB()) {
					 out.addOutput(temp.printSave());
					 out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
					 out.setTarget(sender);
					 Bot.getInstance().sendMessage(Main.getBotSettings().getMainChannel(), temp.printSave());
					 handled = true;
				  }
			   } else if (textItem.getType() == 4) {
				  // instant damage spell/op
				  String province = textItem.getRegex();
				  province = province.substring(0, province.indexOf("[province]"));
				  province = province.replace("[result]", RESULTREGEX);
				  province = message.replaceFirst(province, "");
				  province = StringTools.extractPartialString(PROVINCEREGEX, province);
				  String kd = province.substring(province.indexOf('('));
				  province = province.substring(0, province.indexOf('(')).trim();
				  String result = textItem.getRegex().substring(0, textItem.getRegex().indexOf("[result]"));
				  result = result.replace("[province]", PROVINCEREGEX);
				  result = result.replace("[result]", RESULTREGEX);
				  result = message.replaceFirst(result, "").replace(province, "").replace(kd, "");
				  result = StringTools.extractPartialString(RESULTREGEX, result).replaceAll("[^0-9]", "");
				  InstantDamageSpellOp temp = new InstantDamageSpellOp(province, kd, textItem.getName(),
						Integer.parseInt(result), 1, sender);
				  if (temp.saveToDB()) {
					 out.addOutput(temp.printSave());
					 out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
					 out.setTarget(sender);
					 handled = true;
				  }
			   } else if (textItem.getType() == 5) {
				  // instant damage spell/op without province name in it
				  String result = textItem.getRegex().substring(0, textItem.getRegex().indexOf("[result]"));
				  result = message.replaceFirst(result, "");
				  result = StringTools.extractPartialString(RESULTREGEX, result);
				  InstantDamageSpellOp temp = new InstantDamageSpellOp("", "", textItem.getName(), Integer.parseInt(result), 1,
						sender);
				  if (temp.saveToDB()) {
					 out.addOutput(temp.printSave());
					 out.setDelivery(OutputTypeEnum.RETURN_TO_SOURCE_NOTICE);
					 out.setTarget(sender);
					 handled = true;
				  }
			   }
			   break;
			}
		 }
	  }
	  return handled;
   }

   @Override
   public boolean willHandleText(final String message, final String channel, final String sender) {
	  return true;
   }
}
